Small form on Product page - SurfaceController or ...
# help-with-umbraco
m
I'm trying to show a small form on a product page - users need to put name and email in before they can access extra information - this infomration will go into a custom table. In the U7 version there is a SurfaceController and a Form Partial. For U10 I thought I should use a View Component but couldn't get it to work so went back to SurfaceController but having difficulties there too. On this forum post - https://our.umbraco.com/forum/using-umbraco-and-getting-started/110845-umbraco-10-surface-controllers-multiple-handlers-per-page - they seem to use a combination of both but that's not working either. Could someone just advise which way I should go so at least I don't keep switching from one to the other!!
The form was originally something like this, with Ajax etc, if that makes any difference: @using (Ajax.BeginForm("HandleSubmit", "GatedAccessSurface", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, OnBegin = "GatedAccessSubmit", OnComplete = "GatedAccessComplete", OnSuccess = "GatedAccessSuccess", OnFailure = "GatedAccessFailure" })) { @Html.AntiForgeryToken() @Html.TextBoxFor(x => Model.gac_email, new { autocapitalize = "off", @class = "form-control", placeholder = "email" }) @Html.HiddenFor(x => Model.pageUrl) @Html.HiddenFor(x => Model.fileName) etc etc Submit
h
What exactly isn't working? Are you getting any errors?
m
Sorry - yes lots of errors but I've switched my MO so many times in the last couple of hours I can't say. Going back to view Compontent methodology now to see what the errors are. Just thought it might be obvious to wiser minds whether or not one way was better than the other. Back in a sec with errors, thanks
Currently getting
Copy code
InvalidOperationException: Could not find a Surface controller route in the RouteTable for controller name GatedAccessSurface
Umbraco.Cms.Web.Website.Routing.UmbracoRouteValueTransformer.HandlePostedValues(PostedDataProxyInfo postedInfo, HttpContext httpContext)
I don't have any surface controller set up at the moment. I just have \App_Code\Components\GatedAccess\GatedAccessViewComponent.cs
Copy code
namespace Website.App_Code.Components.GatedAccess
{
    public class GatedAccessViewComponent : ViewComponent
    {
        private readonly IUmbracoContextAccessor _umbracoContextAccessor;
        private readonly IUmbracoContextFactory _umbracoContextFactory;

        public GatedAccessViewComponent(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoContextFactory umbracoContextFactory)
        {
            _umbracoContextAccessor = umbracoContextAccessor;
            _umbracoContextFactory = umbracoContextFactory;
        }

        public IViewComponentResult Invoke()
        {
            GatedAccessItem model = new GatedAccessItem();           
            var content = _umbracoContextAccessor.GetRequiredUmbracoContext().PublishedRequest.PublishedContent;
            model.pageUrl = content.Url();
            model.fileName = String.Empty;
etc etc
            model.gac_optin = false;

            return View(model);
        }
    }
}
and a file in Views/Shared/Components/GatedAccess:
Copy code
@using System.Web;
@using Website.App_Code.Controllers;
@using Website.App_Code.Models;

@model GatedAccessItem

    <div class="gated-access-block">       
            @Html.TextBoxFor(m => m.gac_email, new { autocapitalize = "off", @class = "form-control", placeholder = Model.emailLabelAlias })          
            @Html.TextBoxFor(m => m.gac_name, new { @class = "form-control", placeholder = "name" })
            @Html.TextBoxFor(m => m.gac_companyName, new { @class = "form-control", placeholder = "company" })        
       </div>
Called in the View with
Copy code
async void VideoGated()
    {      
            if (Model.Value<bool>("gated")) // && !GatedAccessLogic.UserIsRegistered())
            {               
                @await Component.InvokeAsync("GatedAccess")               
            }      
    }
So now I'm thinking maybe I do need a SurfaceController but then do I not use the View Component at all ?
h
The surface controller is what you post to, a viewcomponent is just a view (similar to a partial view but is more a means to separate code out of partial views into a class)
m
So I use the View Component part to display the form and then the SurfaceController to process what is posted? Why is it looking for a SurfaceController just to display it then?
h
Is there a form wrapped around your VideoGated() function call? does that refrence a surface controller
m
In the page View? No. The form is in Shared/GatedAccess/Default.cshtml - from when I was using just ViewComponent.The original u7 code has it in Views/Parials/Forms/GatedAccessForm using Ajax liks so: @inherits Umbraco.Web.Mvc.UmbracoViewPage @using (Ajax.BeginForm("HandleSubmit", "GatedAccessSurface", new AjaxOptions { HttpMethod = "POST", InsertionMode = InsertionMode.Replace, OnBegin = "GatedAccessSubmit", OnComplete = "GatedAccessComplete", OnSuccess = "GatedAccessSuccess", OnFailure = "GatedAccessFailure" })) { @Html.AntiForgeryToken() @Html.TextBoxFor(x => Model.gac_email, new { autocapitalize = "off", @class = "form-control", placeholder = umbraco.library.GetDictionaryItem(Model.emailLabelAlias) }) @Html.HiddenFor(x => Model.type) @Html.TextBoxFor(x => Model.gac_name, new { @class = "form-control", placeholder = umbraco.library.GetDictionaryItem("Gated Access Name Label") }) etc etc submit }
This is what I'm trying to replicate
h
What does your foem definition look like? the code you posted for your viewcomponents view does not contain a form, just some form controls
m
Trying to use Ajax as above. In the page View I have: @await Html.PartialAsync("Index", "GatedAccessSurface") but just getting a 404 on that page. So think I am returning the wrong PartialView at the end of the SurfaceController Index()?
Seem tohave got this working by using the View Component to display the form and then the SurfaceController just handles the submission. Don't know why it was acting up so much earlier - needed to clean and rebuild etc
c
I’ve just seen this thread. That’s how I do it. VC for render and SC for post
m
you can get rid of the magic strings.. in the
beginForm
makes it easier to avoid typos.. and if you use umb tag helpers (https://our.umbraco.com/packages/website-utilities/our-umbraco-taghelpers/) makes the view nice for the fronends who are no doubt used to vue/react now..
Copy code
<form method="post" class="form" our-controller="@(nameof(ContactFormSurfaceController).Replace("Controller", ""))" our-action="@(nameof(ContactFormSurfaceController.SubmitContactForm))" id="frmContact">
...
</form>
you could evn go the extra mile and add the controller/action names to the viewComponent Model to make it super clean...
Copy code
<form method="post" class="form" our-controller="@(Model.FormController)" our-action="@(Model.FormAction)" id="frmContact">
🙂 ( though might not be a Umbraco Ajax Form still!)
m
Wow, cool, I wasn't aware of that package - it looks very handy indeed. Thanks!!
3 Views